package com.zhou.combine.security.config;

import org.apache.http.HttpResponse;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

/**
 * RestTemplateConfiguration
 *
 * @author zhouxianfeng
 * @date 2021-3-23
 */
@Configuration
public class RestTemplateConfiguration {

    @Value("${egsc.comfig.api.client.maxTotal:200}")
    private int clientMaxTotal;

    @Value("${egsc.comfig.api.client.defaultMaxPerRoute:100}")
    private int clientDefaultMaxPerRoute;

    @Value("${egsc.config.api.client.clientRequestTimeout:2000}")
    private int clientConnectRequestTimeout;

    @Value("${egsc.config.api.client.clientRequestSocketTimeout:10000}")
    private int clientRequestSocketTimeout;

    @Value("${egsc.config.api.client.readTimeout:10000}")
    private int clientReadTimeout;

    @Value("${egsc.config.api.client.connectTimeout:5000}")
    private int clientConnectTimeout;

    @Value("${http.client.keep.alive.seconds:60000}")
    private long KEEP_ALIVE_SCEONDS;

    @Bean
    public RestTemplate httpClientRestTemplate() {
        PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager();
        // 总连接数
        pollingConnectionManager.setMaxTotal(this.clientMaxTotal);
        // 同路由的并发数
        pollingConnectionManager.setDefaultMaxPerRoute(this.clientDefaultMaxPerRoute);

        HttpClientBuilder httpClientBuilder = HttpClients.custom().disableRedirectHandling();
        httpClientBuilder.setConnectionManager(pollingConnectionManager);
        //这里关掉重试
        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler());
        // 保持长连接配置，需要在头添加Keep-Alive
        httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy() {
            @Override
            public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {
                long keepAlive = super.getKeepAliveDuration(response, context);
                if (keepAlive == -1) {
                    keepAlive = KEEP_ALIVE_SCEONDS;
                }
                return keepAlive;
            }
        });
        CloseableHttpClient httpClient = httpClientBuilder.build();

        // httpClient连接配置，底层是配置RequestConfig
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        /* 连接超时
         * 这定义了通过网络与服务器建立连接的超时时间。
         * Httpclient包中通过一个异步线程去创建与服务器的socket连接，这就是该socket连接的超时时间，
         * 假设：访问一个IP，192.168.10.100，这个IP不存在或者响应太慢，那么将会返回
         * java.net.SocketTimeoutException: connect timed out
         */
        clientHttpRequestFactory.setConnectTimeout(this.clientConnectTimeout);
        /* 数据读取超时时间，即SocketTimeout
         * 指的是连接上一个url，获取response的返回等待时间，假设：url程序中存在阻塞、或者response
         * 返回的文件内容太大，在指定的时间内没有读完，则出现
         * java.net.SocketTimeoutException: Read timed out
         */
        clientHttpRequestFactory.setReadTimeout(this.clientReadTimeout);
        /* 连接不够用的等待时间，不宜过长，必须设置，比如连接不够用时，时间过长将是灾难性的
         * 从连接池中获取连接的超时时间，假设：连接池中已经使用的连接数等于setMaxTotal，新来的线程在等待1*1000
         * 后超时，错误内容：org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
         */
        clientHttpRequestFactory.setConnectionRequestTimeout(this.clientConnectRequestTimeout);
        // 缓冲请求数据，默认值是true。通过POST或者PUT大量发送数据时，建议将此属性更改为false，以免耗尽内存。
        // clientHttpRequestFactory.setBufferRequestBody(false);


        RestTemplate restTemplate1 = new RestTemplate(clientHttpRequestFactory);
        for (HttpMessageConverter<?> converter : restTemplate1.getMessageConverters()) {
            if (converter instanceof StringHttpMessageConverter) {
                ((StringHttpMessageConverter) converter).setWriteAcceptCharset(false);
            }
        }
        return restTemplate1;
    }
}
